package server;

import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.util.List;
import java.util.ArrayList;
import java.util.UUID;

import utils.Common;

public class CallbackHandler extends UnicastRemoteObject implements ICallbackHandler {

	// ATTRIBUTES
    /**
     * 
     */
    private static final long serialVersionUID = -6164046399752149941L;
    private List<ClientWrapper> clients;

    // CONSTRUCTORS
    public CallbackHandler() throws RemoteException {
            super();

            clients = new ArrayList<ClientWrapper>();
    }

    @Override
    public synchronized boolean lockCell(int abscissa, int ordinate, UUID uniqueId) throws RemoteException {

            ClientWrapper client = this.getClientWrapperByUniqueId(uniqueId);
            client.setLockedCellAbscissa(abscissa);
            client.setLockedCellOrdinate(ordinate);

            if (broadcastLockedCell(abscissa, ordinate, client)) {
                    System.out.println(String.format("Cell [%s,%s]: LOCKED by %s!", client.getLockedCellAbscissa(), client.getLockedCellOrdinate(), client.getIpAddress()));
                    return true;
            }
            System.out .println(String.format("Cell [%s,%s]: LOCK FAILED!", client.getLockedCellAbscissa(), client.getLockedCellOrdinate()));
            return false;

    }

    @Override
    public synchronized void registerForCallback(
                    client.ICallbackHandler callbackClientObject, UUID uniqueId)
                    throws RemoteException {

            if (!handleClients(callbackClientObject, "contains")) {
                    String[] clientIpAndPort = Common.getMatches(
                                                    "\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}:\\d+",
                                                    callbackClientObject.toString()).get(0).split(":");
                    this.clients.add(new ClientWrapper(clientIpAndPort[0],
                                    clientIpAndPort[1], callbackClientObject, uniqueId));
            }

            System.out.println("Registed new client");

    }

    @Override
    public synchronized void unregisterForCallback(
                    client.ICallbackHandler callbackClientObject)
                    throws RemoteException {

            if (handleClients(callbackClientObject, "remove")) {
                    System.out.println("Unreisted client");
            } else {
                    System.out.println("Unregister: Client wasn t registed");
            }
    }

    @Override
    public synchronized String getPrecedentOperationResult(String expression,
                    UUID uniqueId) throws RemoteException {

            List<String> matches = Common.getMatches("\\d|[\\*/]", expression);
            boolean doProduct = false, doDivision = false;
            int aux = 0;

            for (String macth : matches) {
                    switch (macth) {
                    case "*":
                            doProduct = true;
                            break;
                    case "/":
                            doDivision = true;
                            break;
                    default:
                            if (doProduct) {
                                    aux *= Integer.parseInt(macth);
                                    doProduct = false;
                            } else if (doDivision) {
                                    if (Integer.parseInt(macth) == 0) {
                                            return null;
                                    }
                                    aux /= Integer.parseInt(macth);
                                    doDivision = false;
                            } else {
                                    aux = Integer.parseInt(macth);
                            }
                            break;
                    }
            }
            System.out.println("Avaliada a expressao '" + expression
                            + "' com o resultado: " + Integer.toString(aux));

            return Integer.toString(aux);
    }

    @Override
    public synchronized void updateColaboratorsCell(UUID uniqueId, String value)
                    throws java.rmi.RemoteException {

            ClientWrapper wrapper = this.getClientWrapperByUniqueId(uniqueId);
            this.broadcastNewCellValue(value, wrapper);

    }

    private synchronized void broadcastNewCellValue(String newCellValue, ClientWrapper wrapper) {
            
            ClientWrapper clienteN;
            
            for (ClientWrapper auxWrapper : clients) {
                    
                    if (!auxWrapper.getUniqueIdentifier().equals(wrapper.getUniqueIdentifier()))
                            auxWrapper.updateCellValue(newCellValue, wrapper.getLockedCellAbscissa(), wrapper.getLockedCellOrdinate());
            }
            
    }

    // public synchronized void doCallbacks() throws RemoteException {
    //
    // // make callback to each registered client
    // System.out.println("**************************************\n"
    // + "Callbacks initiated ---");
    // for (int i = 0; i < clients.size(); i++) {
    // System.out.println("doing " + i + "-th callback\n");
    // // convert the vector object to a callback object
    // ClientWrapper nextClient = clients.get(i);
    // // invoke the callback method
    // nextClient.notifyMe("Numero de clientes registados = "
    // + clients.size());
    // }// end for
    // System.out.println("********************************\n"
    // + "Server completed callbacks ---");
    // }

    private synchronized boolean broadcastLockedCell(int abscissa,
                    int ordinate, ClientWrapper client) {

            for (ClientWrapper wrapper : clients) {
                    if (!wrapper.getUniqueIdentifier().equals(client.getUniqueIdentifier())) {
                            //wrapper.notifyMe(String.format("The cell [%s,%s] is locked @%s", abscissa,ordinate, client.getIpAddress()));
                            wrapper.lockCell(abscissa, ordinate);
                    }
            }

            return true;
    }

    // private synchronized boolean broadcastNewCellValue()

    private synchronized boolean handleClients(
                    client.ICallbackHandler callbackHandler, String function) {

            if (function.equals("contains")) {
                    for (ClientWrapper wrapper : this.clients) {
                            if (wrapper.getCallbackHandler().equals(callbackHandler)) {
                                    return true;
                            }
                    }
                    return false;
            } else {
                    for (ClientWrapper wrapper : this.clients) {
                            if (wrapper.getCallbackHandler().equals(callbackHandler)) {
                                    return this.clients.remove(wrapper);
                            }
                    }
                    return false;
            }
    }

    private synchronized ClientWrapper getClientWrapperByUniqueId(UUID uniqueId) {
            for (ClientWrapper wrapper : this.clients) {
                    if (wrapper.getUniqueIdentifier().equals(uniqueId)) {
                            return wrapper;
                    }
            }
            return null;
    }

}
